/* ======================================== INCLUDES ======================================== */
#include <stdlib.h>
#include <string.h>

#include "bsp_leds.h"

/* ======================================== MACROS ========================================== */
#define WS2812_NUM      60
#define WS2812_NUM_BIT  (24 + 1) // 24bit数据 + 1bit空
#define WS2812_RESET    300

#define WS2812_MODE_SWITHCHING      10

// PWM占空比控制WS2812的0或1码
#define WS2812_CODE_0   (30u)
#define WS2812_CODE_1   (60u)

#define WS2812_TIM      &htim1
#define WS2812_CHANNEL  TIM_CHANNEL_1
#define WS2812_Delay    HAL_Delay

/* ======================================== GLOBAL VARIABLES ================================ */
extern TIM_HandleTypeDef htim1;
extern TIM_HandleTypeDef htim2;

/* ======================================== LOCAL VARIABLES ================================= */
static uint32_t WS2812_Data[WS2812_NUM] = {0};
static uint32_t WS2812_SendBuf0[WS2812_NUM_BIT] = {0};
static uint32_t WS2812_SendBuf1[WS2812_NUM_BIT] = {0};
static const uint32_t WS2812_Rst[WS2812_RESET] = {0};

// 一些状态机
static volatile uint8_t WS2812_En = 0;      // 避免与DMA冲突
static uint8_t WS2812_Current_Count = 0;    // 记录要亮多少颗灯
static uint8_t last_light_count = 0;        // 当前正在显示多少颗灯
static light_mode_typ Current_Mode;         // 当前彩带模式
/* ======================================== INTERNAL FUNCTIONS ============================== */

/**
 * @brief 清除所有LED数据
 */
static inline void WS2812_ClearAll(void)
{
    memset(WS2812_Data, 0, sizeof(WS2812_Data));
}

/**
 * @brief 将32位颜色数据转为WS2812 PWM数据
 */
static inline void WS2812_ColorToPwm(uint32_t color, uint32_t *buf)
{
    for (int i = 0; i < 24; i++)
    {
        buf[i] = (color & (1 << 23)) ? WS2812_CODE_1 : WS2812_CODE_0;
        color <<= 1;
    }
    buf[24] = 0;
}


/**
 * @brief 发送单个LED数据
 */
static void WS2812_SendLed(uint8_t idx)
{
    static uint8_t pingpong = 0;

    if (idx == WS2812_NUM)
    {
        HAL_TIM_PWM_Stop_DMA(WS2812_TIM, WS2812_CHANNEL);
        WS2812_En = 0;
        return;
    }

    if (pingpong == 0)
    {
        HAL_TIM_PWM_Stop_DMA(WS2812_TIM, WS2812_CHANNEL);
        HAL_TIM_PWM_Start_DMA(WS2812_TIM, WS2812_CHANNEL, WS2812_SendBuf0, WS2812_NUM_BIT);
        WS2812_ColorToPwm(WS2812_Data[idx + 1], WS2812_SendBuf1);
        pingpong = 1;
    }
    else
    {
        HAL_TIM_PWM_Stop_DMA(WS2812_TIM, WS2812_CHANNEL);
        HAL_TIM_PWM_Start_DMA(WS2812_TIM, WS2812_CHANNEL, WS2812_SendBuf1, WS2812_NUM_BIT);
        WS2812_ColorToPwm(WS2812_Data[idx + 1], WS2812_SendBuf0);
        pingpong = 0;
    }
}

/* ============== WS2812的应用层 ============= */
/**
 * @brief 启动传输
 */
static void WS2812_Start(void)
{
    HAL_TIM_PWM_Stop_DMA(WS2812_TIM, WS2812_CHANNEL);
    HAL_TIM_PWM_Start_DMA(WS2812_TIM, WS2812_CHANNEL, (uint32_t *)WS2812_Rst, WS2812_RESET);
    WS2812_ColorToPwm(WS2812_Data[0], WS2812_SendBuf0);
    WS2812_En = 1;
}

/**
 * @brief HSV转GRB颜色
 */
static inline uint32_t WS2812_HsvToGrb(uint16_t hue)
{
    uint8_t r = 0, g = 0, b = 0;
    uint16_t h = hue / 256;
    uint8_t f = hue % 256;
    uint8_t p = 0, q = 255 - f, t = f;

    switch (h)
    {
        case 0: r = 255; g = t; b = p; break;
        case 1: r = q; g = 255; b = p; break;
        case 2: r = p; g = 255; b = t; break;
        case 3: r = p; g = q; b = 255; break;
        case 4: r = t; g = p; b = 255; break;
        case 5: r = 255; g = p; b = q; break;
    }
    return ((uint32_t)g << 16) | ((uint32_t)r << 8) | b;
}

/**
 * @brief 更新显示
 */
static void WS2812_Show(void)
{
    if (WS2812_En == 0)
    {
        WS2812_Start();
    }
}

static void WS2812_Classic_Running_Light(void)
{
    static uint16_t delay_time = 0;
    static uint8_t pattern_shift = 0;
    const uint32_t colors[] = {0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF};

    if (delay_time >= 80)
    {
        WS2812_ClearAll();

        for (int i = 0; i < WS2812_NUM; i++)
        {
            WS2812_Data[i] = colors[(i + pattern_shift) % 5];
        }

        WS2812_Show();

        pattern_shift++;
        if (pattern_shift >= 5) pattern_shift = 0;
        delay_time = 0;
    }
    delay_time++;
}

static void WS2812_Color_Wanderer(void)
{
    static uint32_t pos = 0;
    static int8_t dir = 1;
    static uint16_t hue = 0;
    const uint8_t fade_steps[5] = {128, 64, 32, 16, 8};

    WS2812_ClearAll();

    uint32_t color = WS2812_HsvToGrb(hue);
    uint8_t r = (color >> 8) & 0xFF;
    uint8_t g = (color >> 16) & 0xFF;
    uint8_t b = color & 0xFF;

    WS2812_Data[pos] = color;

    for (int i = 0; i < 5; i++)
    {
        uint8_t tr = (r * fade_steps[i]) >> 8;
        uint8_t tg = (g * fade_steps[i]) >> 8;
        uint8_t tb = (b * fade_steps[i]) >> 8;
        uint32_t tailColor = ((uint32_t)tg << 16) | ((uint32_t)tr << 8) | tb;

        if (dir > 0 && pos > i)
            WS2812_Data[pos - (i + 1)] = tailColor;
        else if (dir < 0 && pos + (i + 1) < WS2812_NUM)
            WS2812_Data[pos + (i + 1)] = tailColor;
    }

    WS2812_Show();

    pos += dir;
    hue += 20;
    if (hue >= 1536) hue = 0;

    if (pos >= WS2812_NUM - 1) dir = -1;
    if (pos == 0) dir = 1;
}

static void WS2812_Dual_Line_Running_Random(void)
{
    static uint32_t line1_pos = 0;
    static uint32_t line2_pos = WS2812_NUM - 1;
    static int8_t line1_dir = 1;
    static int8_t line2_dir = -1;
    static uint32_t step_count = 0;
    static uint32_t color_shift = 0;
    static uint8_t speed_level = 2;
    const uint8_t speed_table[] = {1, 2, 4};
    const uint32_t colors[] = {0xFF0000, 0x0000FF, 0x00FF00, 0xFFFF00, 0xFF00FF, 0x00FFFF};
    const uint32_t color_count = sizeof(colors) / sizeof(colors[0]);
    const uint8_t fade_steps[5] = {128, 64, 32, 16, 8};

    WS2812_ClearAll();

    uint32_t color1_index = color_shift % color_count;
    uint32_t color2_index = (color_shift + 1) % color_count;

    // 第一条线
    {
        uint32_t color = colors[color1_index];
        uint8_t r = (color >> 16) & 0xFF;
        uint8_t g = (color >> 8) & 0xFF;
        uint8_t b = color & 0xFF;
        WS2812_Data[line1_pos] = color;
        for (int i = 0; i < 5; i++) {
            int32_t tail_pos = (int32_t)line1_pos - (i + 1) * line1_dir;
            if (tail_pos >= 0 && tail_pos < WS2812_NUM) {
                uint8_t tr = (r * fade_steps[i]) >> 8;
                uint8_t tg = (g * fade_steps[i]) >> 8;
                uint8_t tb = (b * fade_steps[i]) >> 8;
                WS2812_Data[tail_pos] = ((uint32_t)tg << 16) | ((uint32_t)tr << 8) | tb;
            }
        }
    }

    // 第二条线
    {
        uint32_t color = colors[color2_index];
        uint8_t r = (color >> 16) & 0xFF;
        uint8_t g = (color >> 8) & 0xFF;
        uint8_t b = color & 0xFF;
        WS2812_Data[line2_pos] = color;
        for (int i = 0; i < 5; i++) {
            int32_t tail_pos = (int32_t)line2_pos - (i + 1) * line2_dir;
            if (tail_pos >= 0 && tail_pos < WS2812_NUM) {
                uint8_t tr = (r * fade_steps[i]) >> 8;
                uint8_t tg = (g * fade_steps[i]) >> 8;
                uint8_t tb = (b * fade_steps[i]) >> 8;
                WS2812_Data[tail_pos] = ((uint32_t)tg << 16) | ((uint32_t)tr << 8) | tb;
            }
        }
    }

    WS2812_Show();

    static uint32_t tick = 0;
    if (++tick >= speed_table[speed_level]) {
        tick = 0;
        line1_pos += line1_dir;
        line2_pos += line2_dir;
        step_count++;

        if (step_count % 10 == 0) color_shift = (color_shift + 1) % color_count;

        if (line1_pos >= WS2812_NUM) line1_pos = 0;
        if (line2_pos >= WS2812_NUM || line2_pos == 0xFFFFFFFF) line2_pos = WS2812_NUM - 1;

        if (step_count % 200 == 0) speed_level = rand() % 3;
    }
}

static void WS2812_Light_Music_Off(void)
{
    if (last_light_count < WS2812_Current_Count)
    {
        last_light_count = WS2812_Current_Count;
        WS2812_Current_Count = 0;
    }

    if (last_light_count > 0)
    {
        WS2812_Data[last_light_count - 1] = 0;
        last_light_count--;
    }
    else
    {
        WS2812_ClearAll();
    }

    WS2812_Show();
}

/* ======================================== EXTERNAL FUNCTIONS ============================== */
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
    static uint32_t j = 0;

    if (htim->Instance == TIM1 && WS2812_En)
    {
        WS2812_SendLed(j++);
        if (j > WS2812_NUM) j = 0;
    }
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{// 定时器中断1ms触发一次（这里判断是哪个模式）
    static uint8_t time_count = 0;
    if (htim->Instance == TIM2)
    {
        time_count++;
        if (time_count >= WS2812_MODE_SWITHCHING)
        {
            switch (Current_Mode)
            {
                case LIGHT_MODE_CLASSIC_MARQUEE:
                    WS2812_Classic_Running_Light();
                    break;
                case LIGHT_MODE_ONE_MARQUEE:
                    WS2812_Color_Wanderer();
                    break;
                case LIGHT_MODE_TWU_MARQUEE:
                    WS2812_Dual_Line_Running_Random();
                    break;
                case LIGHT_MODE_MUSIC:
                    WS2812_Light_Music_Off();
                    break;
            }
            time_count = 0;
        }
    }
}

void WS2812_Light_Music_Setcount(uint8_t count)
{// 彩灯亮的时候渐变、根据麦克风等级记录当前灯到全局变量
    WS2812_Current_Count = count;

    static uint16_t hue = 0;
    hue += 6;
    if (hue >= 1536) hue = 0;

    uint32_t color = WS2812_HsvToGrb(hue);
    uint8_t r = (color >> 8) & 0xFF;
    uint8_t g = (color >> 16) & 0xFF;
    uint8_t b = color & 0xFF;

    WS2812_ClearAll();

    for (int i = 0; i < count && i < WS2812_NUM; i++)
    {
        uint8_t bright = 64 + ((191 * (count - i)) / (count ? count : 1));
        uint8_t rr = (r * bright) >> 8;
        uint8_t gg = (g * bright) >> 8;
        uint8_t bb = (b * bright) >> 8;
        WS2812_Data[i] = ((uint32_t)gg << 16) | ((uint32_t)rr << 8) | bb;
    }

    WS2812_Show();
}

void light_mode_select(light_mode_typ mode)
{
    Current_Mode = mode;
}

light_mode_typ light_get_mode(void)
{
    return Current_Mode;
}

uint8_t get_current_lighting(void)
{
    return last_light_count;
}

void WS2812_Init(void)
{
    WS2812_ClearAll();
    WS2812_Start();
    WS2812_Delay(100);
    HAL_TIM_Base_Start_IT(&htim2);
}



#if 0
void WS2812_Breath_Simple(void)
{
    static uint32_t color = 0;
    WS2812_ClearAll();

    if (0x00FF00 < color)
        color = 0;
    else
        color += 0x001100;

    for (int i = 0; i < WS2812_NUM; i++)
        WS2812_Data[i] = color;

    WS2812_Show();
}
#endif
